
#include <stdlib.h>
#include <string.h>
#include "gkt_upgrade.h"
#include "gkt_version.h"
#include "gkt_flash_partition.h"
#include "gkt_network.h"
#include "gkt_malloc.h"
#include "window_common.h"


#define WINDOW_UPGRADE_TEST 1

#define WINDOW_UPGREDE_RETRY_COUNT_MAX 3
#define WINDOW_UPGREDE_DELAY_RETURN_TIME_MS 2000
#define WINDOW_UPGRADE_WRITE_FLASH_DATA_SIZE 512
#define WINDOW_UPGRADE_TIMEOUT_TIME_MS (30*60*1000)
enum {
	TIMER_ID_TIMEOUT = 0,
	TIMER_ID_MAX
};

enum {
	WIDGET_ID_TITLE = 0,
	WIDGET_ID_HINT,
	WIDGET_ID_KEEP_POWER,
	WIDGET_ID_RETURN,
	WIDGET_ID_MAX
};

typedef struct {
	uint32_t guf_total_size;
	uint32_t receive_data_size;
	uint32_t offset_addr_last;
	uint8_t *write_flash_data;
	uint32_t write_flash_addr;
	uint32_t retry_count;
	float progress;
#if !WINDOW_UPGRADE_TEST
	uint16_t crc_init;
	const gkt_flash_partition_desc_s *p_partition_desc;
	uint8_t cid_app;
	uint8_t cid_voice_chn;
	uint8_t cid_voice_eng;
#endif
}window_upgrade_s;
window_upgrade_s s_window_upgrade;

GKT_WINDOW_WIDGET_DESCS_DEFINE(upgrade) = {
	[WIDGET_ID_TITLE] = GKT_WIDGET_LABEL_DESC(
		GKT_WIDGET_F_ALIGN_MIDDLE | GKT_WIDGET_F_ALIGN_CENTER | GKT_WIDGET_F_TITLE_MARK,
		WINDOW_FONT_NORMAL_SIZE,
		WINDOW_TITLE_FG_COLOR, WINDOW_TITLE_BG_COLOR,
		0, 0,
		GKT_SCREEN_WIDTH, WINDOW_WIDGET_NORMAL_HEIGHT,
		WINDOW_TEXT_ID_system_upgrade),
	[WIDGET_ID_HINT] = GKT_WIDGET_LABEL_DESC(
		GKT_WIDGET_F_ALIGN_MIDDLE,
		WINDOW_FONT_NORMAL_SIZE,
		WINDOW_DEFAULT_FG_COLOR, WINDOW_DEFAULT_BG_COLOR,
		0, WINDOW_WIDGET_NORMAL_HEIGHT,
		GKT_SCREEN_WIDTH, WINDOW_WIDGET_NORMAL_HEIGHT,
		NULL),
	[WIDGET_ID_KEEP_POWER] = GKT_WIDGET_LABEL_DESC(
		GKT_WIDGET_F_ALIGN_MIDDLE,
		WINDOW_FONT_NORMAL_SIZE,
		WINDOW_HINT_IMPORTANT_FG_COLOR, WINDOW_DEFAULT_BG_COLOR,
		0, WINDOW_WIDGET_NORMAL_HEIGHT * 2,
		WINDOW_WIDGET_DEFAULT_WIDTH, WINDOW_WIDGET_NORMAL_HEIGHT,
		WINDOW_TEXT_ID_please_keep_the_power_on
	),
	[WIDGET_ID_RETURN] = GKT_WIDGET_LABEL_DESC(
		GKT_WIDGET_F_ALIGN_MIDDLE,
		WINDOW_FONT_NORMAL_SIZE,
		WINDOW_DEFAULT_FG_COLOR, WINDOW_DEFAULT_BG_COLOR,
		0, WINDOW_WIDGET_NORMAL_HEIGHT * 3,
		WINDOW_WIDGET_DEFAULT_WIDTH, WINDOW_WIDGET_NORMAL_HEIGHT,
		WINDOW_TEXT_ID_return
	)
};

static int window_upgrade_init()
{
	s_window_upgrade.guf_total_size = 0;
	s_window_upgrade.offset_addr_last = 0;
	s_window_upgrade.receive_data_size = 0;
	s_window_upgrade.retry_count = 0;
	s_window_upgrade.progress = 0;

	if (NULL == s_window_upgrade.write_flash_data) {
		s_window_upgrade.write_flash_data = 
			(uint8_t*)gkt_malloc(WINDOW_UPGRADE_WRITE_FLASH_DATA_SIZE);
		if (NULL == s_window_upgrade.write_flash_data) {
			gkt_error("window_upgrade: gkt_calloc failed!\n");
			return GKT_ENOMEMORY;
		}
	}
#if !WINDOW_UPGRADE_TEST
	uint8_t mid;
	if (gkt_flash_partition_id(GKT_CONFIG_FLASH_PARTITION_SHARED_NAME, &mid)) {
		s_window_upgrade.cid_app = GKT_FLASH_PARTITION_CID(mid, 0);
		s_window_upgrade.p_partition_desc = gkt_flash_partition_desc(s_window_upgrade.cid_app);
	}
	else {
		gkt_error("table_backup_set get partition fail\n");
		return GKT_ENORESOURCE;
	}
	s_window_upgrade.crc_init = 0xFFFF;
#endif

	return GKT_SUCCESS;
}
static void window_upgrade_complete(window_result_e result)
{
	uint32_t hint_type;

	gkt_info("window_upgrade_complete: result(%u)\n", result);

	if (WINDOW_RESULT_OPERATE_SUCCESS == result)
		hint_type = WINDOW_DIALOG_HINT_SUCCESS;
	else if (WINDOW_RESULT_OPERATE_TIMEOUT == result)
		hint_type = WINDOW_DIALOG_HINT_TIMEOUT;
	else
		hint_type = WINDOW_DIALOG_HINT_FAILED;

	if (NULL != s_window_upgrade.write_flash_data) {
		free(s_window_upgrade.write_flash_data);
		s_window_upgrade.write_flash_data = NULL;
	}

	gkt_window_timeout_enable("window_upgrade_complete - complete");
	gkt_window_system_standby_enable();

	gkt_window_timer_clear(TIMER_ID_TIMEOUT);

	window_dialog_show_big_prompt(hint_type, 
			WINDOW_TEXT_ID_being_upgrade_firmware, 
			gc_window_result_text_ids[result],
			gc_window_result_voice_ids[result]);
	gkt_window_exit();

}

static void window_upgrade_update_partition_table()
{
	
}
static void window_upgrade_update_progress(int receive_data_size)
{
	float ratio;
	int progress_last;
	char progress[8];

	progress_last = (int)s_window_upgrade.progress;
	ratio = (s_window_upgrade.guf_total_size-sizeof(gkt_guf_header_s)) / 100;
	s_window_upgrade.progress += (receive_data_size / ratio);

	gkt_trace("window_upgrade_update_progress: ratio(%f), data_size(%u), s_window_upgrade.progress(%.2f)\n", 
		ratio, receive_data_size, s_window_upgrade.progress);
	if ((int)s_window_upgrade.progress > progress_last) {
		memset(progress, 0x00, sizeof(progress));
		gkt_snprintf(progress, sizeof(progress), "%d%%", (int)s_window_upgrade.progress);
		gkt_widget_set_data_by_id(WIDGET_ID_HINT, (uint32_t)progress);
	}
}
static int window_upgrade_data_process(uint32_t data)
{
	gkt_network_mcu_upgrade_data_s upgrade_data;
	gkt_guf_header_s guf_header;
	//int retval = GKT_SUCCESS;

	upgrade_data = *((gkt_network_mcu_upgrade_data_s*)data);

	gkt_info("window_upgrade_data_process: (offset - size - offset_addr_last) (%u - %u - %u)\n", 
		upgrade_data.offset, upgrade_data.data_size, s_window_upgrade.offset_addr_last);
	memset(s_window_upgrade.write_flash_data, 0, WINDOW_UPGRADE_WRITE_FLASH_DATA_SIZE);
	s_window_upgrade.receive_data_size += upgrade_data.data_size;
	if (0 == upgrade_data.offset) {
		uint32_t header_size = sizeof(gkt_guf_header_s);
		if (upgrade_data.data_size < header_size) {
			gkt_error("upgrade file packet size less than guf header size(%d)\n", header_size);
			return GKT_EDATALOSS;
		}
		//guf_header
		memcpy(&guf_header, upgrade_data.data_buf, header_size);

		if (upgrade_data.data_size - header_size > 0) {
			memcpy(s_window_upgrade.write_flash_data,
				&upgrade_data.data_buf[header_size], upgrade_data.data_size - header_size);
			s_window_upgrade.receive_data_size = upgrade_data.data_size - header_size;
			s_window_upgrade.offset_addr_last = upgrade_data.data_size;
#if !WINDOW_UPGRADE_TEST
			s_window_upgrade.crc_init = gkt_crc16_ccitt_false(s_window_upgrade.write_flash_data, 
					s_window_upgrade.receive_data_size, &s_window_upgrade.crc_init);
			retval = gkt_flash_partition_write(s_window_upgrade.cid_app, 
					s_window_upgrade.write_flash_data, 0, s_window_upgrade.receive_data_size);
			if (GKT_SUCCESS != retval) {
				gkt_error("window_upgrade_data_process: write flash failed, retval(%d)\n", retval);
				return retval;
			}
#endif
			window_upgrade_update_progress(upgrade_data.data_size - header_size);
		}
	}
	else if (upgrade_data.offset == s_window_upgrade.offset_addr_last) {
		s_window_upgrade.retry_count = 0;

		s_window_upgrade.offset_addr_last = upgrade_data.offset + upgrade_data.data_size;
#if 1
#if !WINDOW_UPGRADE_TEST
		s_window_upgrade.crc_init = gkt_crc16_ccitt_false(s_window_upgrade.write_flash_data, 
				s_window_upgrade.receive_data_size, &s_window_upgrade.crc_init);
		retval = gkt_flash_partition_write(s_window_upgrade.cid_app, 
				s_window_upgrade.write_flash_data, 0, s_window_upgrade.receive_data_size);
		if (GKT_SUCCESS != retval) {
			gkt_error("window_upgrade_data_process: write flash failed, retval(%d)\n", retval);
			return retval;
		}
#endif
		window_upgrade_update_progress(upgrade_data.data_size);
#else
		if ((s_window_upgrade.receive_data_size+upgrade_data.data_size) > 
			WINDOW_UPGRADE_WRITE_FLASH_DATA_SIZE)
		{
			remainder =  (s_window_upgrade.receive_data_size+upgrade_data.data_size) - 
				WINDOW_UPGRADE_WRITE_FLASH_DATA_SIZE;
			memcpy(&s_window_upgrade.write_flash_data[s_window_upgrade.receive_data_size], 
			upgrade_data.data_buf, remainder);
			s_window_upgrade.receive_data_size = upgrade_data.data_size - remainder;
			s_window_upgrade.write_flash_addr += WINDOW_UPGRADE_WRITE_FLASH_DATA_SIZE;
			
		}
		else {
			memcpy(&s_window_upgrade.write_flash_data[s_window_upgrade.receive_data_size], 
			upgrade_data.data_buf, upgrade_data.data_size);
			s_window_upgrade.receive_data_size += upgrade_data.data_size;
		}
#endif
		if ((upgrade_data.offset+upgrade_data.data_size) == s_window_upgrade.guf_total_size) {
			window_upgrade_update_partition_table();
			window_upgrade_complete(WINDOW_RESULT_OPERATE_SUCCESS);
		}
	}
	else if (s_window_upgrade.retry_count <= WINDOW_UPGREDE_RETRY_COUNT_MAX) {
		s_window_upgrade.retry_count++;
		return GKT_SUCCESS;
	}
	else {
		gkt_error("window_upgrade_data_process: data error , retry count (%d)\n", 
				s_window_upgrade.retry_count);
		return GKT_EUNKNOWN;
	}
	gkt_network_mcu_upgrade_data_transfer_confirm();
	return GKT_SUCCESS;
}
static void window_upgrade_system_event_handler(
				uint32_t minor_type, uint32_t type, uint32_t data)
{
	int retval;

	gkt_info("window_upgrade_system: (minor_type - type - data), (%u - %u - %u)\n", 
		minor_type, type, data);

	if (GKT_WINDOW_SYSTEM_NETWORK == minor_type) {
		if (GKT_NETWORK_NOTIFY_TYPE_RUNNING_STATUS == type) {
			if (GKT_NETWORK_RUNNING_ST_UPGRADED == data) {
				//window_upgrade_complete(WINDOW_RESULT_OPERATE_SUCCESS);
				return;
			}
		}
		else if (GKT_NETWORK_NOTIFY_TYPE_MCU_UPGRADE_START == type) {
			//window_common_mask_all();
			gkt_widget_set_data_by_id(WIDGET_ID_HINT, (uint32_t)"0%");
			s_window_upgrade.guf_total_size = data;
			retval = gkt_network_mcu_upgrade_start(GKT_GUF_PACKET_SIZE_MAX);
			if (retval != GKT_SUCCESS) {
				gkt_error("window_upgrade: gkt_network_mcu_upgrade_start failed! retval(%d)\n", retval);
				window_upgrade_complete(WINDOW_RESULT_OPERATE_FAILED);
				return;
			}
#if !WINDOW_UPGRADE_TEST
			gkt_flash_partition_erase(s_window_upgrade.cid_app, 0, s_window_upgrade.p_partition_desc->total_size_KB);
#endif
		}
		else if (GKT_NETWORK_NOTIFY_TYPE_MCU_UPGRADE_DATA == type) {
			retval = window_upgrade_data_process(data);
			if (retval != GKT_SUCCESS) {
				window_upgrade_complete(WINDOW_RESULT_OPERATE_FAILED);
				return;
			}
		}
	}
}

static int window_upgrade_event_handler(
					uint32_t major_type, uint32_t minor_type, 
					uint32_t id, uint32_t data) {

	int retval = GKT_EVENT_HANDLE_CONTINUE;

	gkt_info("window_upgrade_event_handler: (major-minor)(type-data), (%u-%u)(%u-%u)\n", 
		major_type, minor_type, id, data);

	switch (major_type) {
		case GKT_EVENT_WINDOW:
			if (GKT_WINDOW_ACTIVE == minor_type) {
				gkt_window_system_standby_disable();
				gkt_window_timeout_disable("window_upgrade - active");
				window_common_mask_except_return();
				gkt_guf_product_id_s product_id_s = {0, 0, 0, 0};

				product_id_s.hw_mainboard_version = GKT_HW_VERSION_MAIN_INT16;
				product_id_s.hw_daughter_board = GKT_HW_VERSION_DAUGHTER_INT16;
				product_id_s.client_usage = GKT_HW_VERSION_CUSTOMER_INT16;

				if ((GKT_SUCCESS != window_upgrade_init())
					|| GKT_SUCCESS != gkt_network_mcu_upgrade_request(&product_id_s, 0))
				{
					window_upgrade_complete(WINDOW_RESULT_OPERATE_FAILED);
				}
				else {
					gkt_info("window_upgrade_event_handler: init & upgrade_request success\n");
					gkt_widget_set_showable_by_id(WIDGET_ID_KEEP_POWER, 1);
					gkt_widget_show_by_id(WIDGET_ID_KEEP_POWER);
					gkt_window_timer_set(TIMER_ID_TIMEOUT, WINDOW_UPGRADE_TIMEOUT_TIME_MS, 0, 0);
				}
			}
			break;
		case GKT_EVENT_SYSTEM:
			window_upgrade_system_event_handler(minor_type, id, data);
			retval = GKT_EVENT_HANDLE_COMPLETE;
			break;
		case GKT_EVENT_KEY:
			if (GKT_KEY_ASTERISK == minor_type) {
				window_upgrade_complete(WINDOW_RESULT_OPERATE_FAILED);
				retval = GKT_EVENT_HANDLE_COMPLETE; 
			}
			break;
		case GKT_EVENT_TIMER:
			if (TIMER_ID_TIMEOUT == minor_type)
				window_upgrade_complete(WINDOW_RESULT_OPERATE_TIMEOUT);
			break;
		default:
			break;
	}
	return retval;
}


GKT_WINDOW_DESC_DEFINE(upgrade);

